﻿-- oUF_Leafed
-- C J Silverio, ceejceej@gmail.com
-- http://code.google.com/p/leafedmods/
-- Credits: 
--		the oUF framework is by Haste
--		I used oUF_Hypocrisy as a starting point, though little code remains.
--		oUF_Caellian provided major inspiration.
--		oUF_DruidHots gave me the core of the HoT display implementation.
--		Testing by guildmates Elegleep and Awahurs.
--		Status bar texture is Wglass from SharedMedia.
--		Font is RusselSquare.

local settings = Leafed.constants

-- ------------------------------------------------------------------------
-- misc file-scope variables
-- ------------------------------------------------------------------------
local playerClass = select(2, UnitClass("player"))
local plainBackdrop = {bgFile = "Interface\\ChatFrame\\ChatFrameBackground", 
				tile = true, tileSize = 16,
				insets = {left = 0, right = 0, top = 0, bottom = 0},}
	
local select = select
local UnitClass = UnitClass
local UnitIsDead = UnitIsDead
local UnitIsGhost = UnitIsGhost
local UnitIsPlayer = UnitIsPlayer
local UnitIsConnected = UnitIsConnected
local UnitCreatureType = UnitCreatureType
local UnitClassification = UnitClassification

-- ------------------------------------------------------------------------
-- Auras (such as hots or shields) that are interesting to specific classes
-- are displayed as dots on the unit frame. Only auras cast by the player or
-- relevant to the player are shown. Debuffs can be shown too (though this isn't
-- really for poison or cold, etc, which are handled by the usual ouf plugin).
-- ------------------------------------------------------------------------

LIFEBLOOM = "Interface\\Icons\\INV_Misc_Herb_Felblossom"
REGROWTH = "Interface\\Icons\\Spell_Nature_ResistNature"
REJUVENATION = "Interface\\Icons\\Spell_Nature_Rejuvenation"
WILDGROWTH = "Interface\\Icons\\Ability_Druid_Flourish"
RENEW = "Interface\\Icons\\Spell_Holy_Renew"
WEAKENED_SOUL = "Interface\\Icons\\Spell_Holy_AshesToAshes"
PWSHIELD = "Interface\\Icons\\Spell_Holy_PowerWordShield"
HANDOFPROT = "Interface\\Icons\\Spell_Holy_SealOfProtection"
DIVINEPROT = "Interface\\Icons\\Spell_Holy_Restoration"
DIVINESHIELD = "Interface\\Icons\\Spell_Holy_DivineIntervention"
BLOOD_PLAGUE = "Interface\\Icons\\Spell_DeathKnight_BloodPlague"
FROST_FEVER = "Interface\\Icons\\Spell_DeathKnight_FrostFever"

local aurasByClass =
{
	["DRUID"] = { LIFEBLOOM, REJUVENATION, REGROWTH, WILDGROWTH, RENEW },
	["PRIEST"] = { RENEW, PWSHIELD, WEAKENED_SOUL },
	["PALADIN"] = { HANDOFPROT, DIVINEPROT, DIVINESHIELD },
	["DEATHKNIGHT"] = { BLOOD_PLAGUE, FROST_FEVER, },
}

local stateColors =
{
	[LIFEBLOOM] = {
		count = 3,
		color = { r=0, g=1.0, b=0 },
	},
	[REGROWTH] = {
		count = 1,
		color = { r=0, g=1, b=0.75 },
	},
	[REJUVENATION] = {
		count = 1,
		color = { r=1, g=1, b=0 },
	},
	[WILDGROWTH] = {
		count = 1,
		color = { r=1, g=0.5, b=0 },
	},
	[RENEW] = {
		count = 1,
		color = { r=1, g=1, b=.75 },
	},
	[WEAKENED_SOUL] = {
		count = 1,
		color = { r=1, g=0.5, b=0 },
	},
	[PWSHIELD] = {
		count = 1,
		color = { r=1, g=1, b=0 },
	},
	[HANDOFPROT] = {
		count = 1,
		color = { r=1, g=1, b=0 },
	},
	[DIVINEPROT] = {
		count = 1,
		color = { r=0, g=1, b=0.75 },
	},
	[DIVINESHIELD] = {
		count = 1,
		color = { r=0, g=1.0, b=0 },
	},
	[BLOOD_PLAGUE] = {
		count = 1,
		color = { r=1, g=.67, b=.67 },
	},
	[FROST_FEVER] = {
		count = 1,
		color = { r=.67, g=.67, b=1 },
	},
}

-- ------------------------------------------------------------------------
-- utilities
-- ------------------------------------------------------------------------

local formatNumber = function(num)
	if (num > 1000000) then
		return string.format("%.1f", num/1000000).."M"
	elseif (num > 1000) then
		return string.format("%.1f", num/1000).."K"
	else
		return num
	end
end

-- ------------------------------------------------------------------------
-- class icons
-- ------------------------------------------------------------------------
local classIcons = 
{
  ["WARRIOR"] = { 0.00, 0.25, 0.00, 0.25 },
  ["MAGE"] = { 0.25, 0.50, 0.00, 0.25 },
  ["ROGUE"] = { 0.50, 0.75, 0.00, 0.25 },
  ["DRUID"] = { 0.75, 1.00, 0.00, 0.25 },
  ["HUNTER"] = { 0.00, 0.25, 0.25, 0.50 },
  ["SHAMAN"] = { 0.25, 0.50, 0.25, 0.50 },
  ["PRIEST"] = { 0.50, 0.75, 0.25, 0.50 },
  ["WARLOCK"] = { 0.75, 1.00, 0.25, 0.50 },
  ["PALADIN"] = { 0.00, 0.25, 0.50, 0.75 },
  ["DEATHKNIGHT"] = { 0.25, 0.50, 0.50, 0.75 },
 }
 
 local UpdateClassIcon = function(self, event)
	local _, class = UnitClass(self.unit)
	local icon = self.ClassIcon
	local coords = classIcons[class]

	if (class) then
		icon:SetTexCoord(coords[1], coords[2], coords[3], coords[4]);
		icon:Show()
	else
		icon:Hide()
	end
 end

-- ------------------------------------------------------------------------
-- right click
-- ------------------------------------------------------------------------
local menu = function(self)
	local unit = self.unit:sub(1, -2)
	local cunit = self.unit:gsub("(.)", string.upper, 1)

	if (unit == "party" or unit == "partypet") then
		ToggleDropDownMenu(1, nil, _G["PartyMemberFrame"..self.id.."DropDown"], "cursor", 0, 0)
	elseif (_G[cunit.."FrameDropDown"]) then
		ToggleDropDownMenu(1, nil, _G[cunit.."FrameDropDown"], "cursor", 0, 0)
	end
end

-- ------------------------------------------------------------------------
-- class colors & icons, used when 3d portraits aren't
-- data lifted from oUF_Lunar
-- ------------------------------------------------------------------------


local getClassColor = function(unit)
	if (not unit) then return 0, 0, 0 end
	local packed = oUF.colors.class[select(2, UnitClass(unit))]
	if packed then 
		return unpack(packed)
	else
		return 0, 0, 0
	end
end

-- ------------------------------------------------------------------------
-- level and classification update
-- ------------------------------------------------------------------------
local OnUpdateLevel = function(self, unit, name)
	local lvl = UnitLevel(unit)
	local classification = UnitClassification(unit)
	local color = GetQuestDifficultyColor(lvl)

	if lvl <= 0 then lvl = "??" end

	if classification=="worldboss" then
		self.Level:SetText("|cffff0000"..lvl.."|rb")
	elseif classification=="rareelite" then
		self.Level:SetText(lvl.."r+")
		self.Level:SetTextColor(color.r, color.g, color.b)
	elseif classification=="elite" then
		self.Level:SetText(lvl.."+")
		self.Level:SetTextColor(color.r, color.g, color.b)
	elseif classification=="rare" then
		self.Level:SetText(lvl.."r")
		self.Level:SetTextColor(color.r, color.g, color.b)
	else
		if UnitIsConnected(unit) == nil then
			self.Level:SetText("??")
		else
			self.Level:SetText(lvl)
		end
		self.Level:SetTextColor(color.r, color.g, color.b)
	end

	local adjustWidth = 0
	if (self.Level:IsShown()) then
		adjustWidth = self.Level:GetStringWidth() + 5
		self.Name:SetPoint("BOTTOMLEFT", self.Level, "BOTTOMLEFT", adjustWidth, 0)
		self.Name:SetWidth(self:GetWidth() - adjustWidth) -- clip to the frame region
	end
	if (self.UnitDescription) then
		adjustWidth = adjustWidth + self.Name:GetStringWidth() + 2
		self.UnitDescription:SetPoint("BOTTOMLEFT", self.Name, "BOTTOMLEFT",  self.Name:GetStringWidth() + 2, 1)
		self.UnitDescription:SetWidth(self:GetWidth() - adjustWidth)
	end

	-- really should do this just once, when the frame is first populated with unit data
	if false and (not self.Portrait) and Leafed.config.useClassColor then
		r, g, b = getClassColor(unit)
		self:SetBackdropColor(r - .6, g  - .6, b - .6, 0.75)
	end
end

-- ------------------------------------------------------------------------
-- name update
-- ------------------------------------------------------------------------
local OnUpdateName = function(self, event, unit)
	if (self.unit ~= unit) then return end

	local name = UnitName(unit)
	self.Name:SetText(name)
	self.Name:SetTextColor(1,1,1)
	
	OnUpdateLevel(self, unit, name) -- this will ellipsize properly as a side effect
end

local OnUpdateNameDetailed = function(self, event, unit)
	if (self.unit ~= unit) then return end

	local name = UnitName(unit)
	local subtitle
	
	if UnitIsPlayer(unit) then
		subtitle = UnitClass(unit)
	else
		subtitle = UnitCreatureType(unit)
		if (subtitle and subtitle:match("Not Specified")) then -- TODO localization problem!
			subtitle = nil
		end
	end

	if subtitle then	
		self.Name:SetText(name)
		self.UnitDescription:SetText("["..subtitle.."]")
	else
		self.Name:SetText(name)
		self.UnitDescription:SetText()
	end
	self.Name:SetTextColor(1,1,1)
	
	OnUpdateLevel(self, unit, name) -- this will ellipsize properly as a side effect
end

-- ------------------------------------------------------------------------
-- health update
-- ------------------------------------------------------------------------
local PostUpdateHealth = function(self, event, unit, bar, current, max)	 
	local valueTxt = formatNumber(max)
	local statusTxt = nil

	r, g, b = 0.608, 0.165, 0.082
	--r, g, b = bar:GetStatusBarColor() -- use whatever oUF picked for our color
	local alpha = 0
	if (max ~= current) then
		alpha = (max - current)/max + 0.5
		local percentage = floor(current/max*100)
		valueTxt = percentage.."%"
	end
	
	-- are we flipped?
	if bar.invert then
		bar:SetValue(max - current)
	end
	
	if (not UnitIsConnected(unit)) then
		valueTxt = "off"
		alpha = .5
	elseif (UnitIsGhost(unit)) then
		valueTxt = "ghost"
		alpha = .5
	elseif (UnitIsDead(unit)) then
		valueTxt = "dead"
		r, g, b = .25, .25, .25
		alpha = .5
	elseif (UnitIsAFK(unit)) then
		statusTxt = "afk"
	elseif (UnitIsTapped(unit) and not UnitIsTappedByPlayer(unit)) then
		statusTxt = "tapped"
	end
	
	if (bar.compact) then
		if statusTxt then
			bar.value:SetText(statusTxt)
		else
			bar.value:SetText(valueTxt)
		end
	else
		bar.status:SetText(statusTxt)
		bar.value:SetText(valueTxt)
	end
	--bar:SetStatusBarColor(0.681, 0.185, 0.093, alpha)
	bar:SetStatusBarColor(r, g, b, alpha)	
	
	-- why? necessary?
	self:UNIT_NAME_UPDATE(event, unit)
end

local function OnUnitFlagChange(self, event, unit)
	if not unit or self.unit ~= unit then return end
	local bar = self.Health
	if not bar then return end

	if UnitIsAFK(unit) then
		bar.status:SetText("AFK")
	else
		bar.status:SetText(nil)
	end
end

-- ------------------------------------------------------------------------
-- ------------------------------------------------------------------------
-- power callbacks
-- ------------------------------------------------------------------------
local PreUpdatePower = function(self, event, unit)
	if (self.unit ~= unit) then return end
	local pNum, pType = UnitPowerType(unit)

	local color = self.colors.power[pType]
	if color then
		self.Power:SetStatusBarColor(color[1], color[2], color[3])
	end	
end

local PostUpdatePower = function(self, event, unit, bar, current, max)
	if (self.unit ~= unit) then return end
	local pNum, pType = UnitPowerType(unit)

	if (unit == "player") and (playerClass == "DRUID") and (pNum ~= 0) then
		-- If the player is a shape-shifted druid, mana percent is shown in the value box.
		current = UnitPower("player", 0)
		max = UnitPowerMax("player", 0)
		bar.value:SetText(floor(100 * current / max).."%")
	elseif (max == 0) then
		bar.value:SetText()
	elseif UnitIsPlayer(unit)==nil then 
		bar.value:SetText()
	elseif (current == max) then
		-- show value if at max, percent otherwise, just as with health
		bar.value:SetText(max)
	else
		bar.value:SetText(floor(100 * current / max).."%")
	end
end

local UpdateDruidPower = function(self)
	local pNum, pType = UnitPowerType('player')
	if pNum == 0 then
		-- not shapeshifted, no work to do
		return
	end

	local current = UnitPower("player", 0)
	local max = UnitPowerMax("player", 0)
	self.Power.value:SetText(floor(100 * current / max).."%")
end


local state = {} -- keep this allocated
local function OnUnitAuraChange(self, event, unit)
	if self.unit ~= unit then return end

	local statesToIndicate = aurasByClass[playerClass]
	if (not statesToIndicate) or (#statesToIndicate == 0) then
		return
	end

	local i = 1
	for i = 1,#statesToIndicate do
		state[statesToIndicate[i]] = 0
	end

	while true do
		local name, _, texture, count, _, _, expirationTime, caster = UnitBuff(unit, i, true)
		local isMine = (caster == "player")
		if not name then 
			break 
		end
		if isMine and stateColors[texture] then
			local timeleft = expirationTime - GetTime()
			if texture == LIFEBLOOM then -- TODO this is hacky; abstract it
				state[texture] = count
			else
				state[texture] = 1
			end
		end
		i = i + 1
	end

	-- TODO refactor common stuff into function
	i = 1
	while true do
		local name, _, texture, count, _, _, expirationTime, caster = UnitDebuff(unit, i)
		local isMine = (caster == "player")
		if not name then 
			break 
		end
		if isMine and stateColors[texture] then
			state[texture] = 1
		end
		i = i + 1
	end

	for k,v in pairs(state) do
		for i=1,stateColors[k].count do
			if (self.StateIndicators[k] and self.StateIndicators[k][i]) then
				if i <= state[k] then
					self.StateIndicators[k][i]:Show()
				else
					self.StateIndicators[k][i]:Hide()
				end
			end
		end
	end
end

-- ------------------------------------------------------------------------
-- misc callbacks
-- ------------------------------------------------------------------------

local OnUpdateCombat = function(self, event)
	-- make the frames more schematic and less decorative in combat
	if UnitAffectingCombat("player") then
		if self.Portrait then self.Portrait:SetAlpha(math.min(.25, settings.portraitAlpha)) end
	else
		if self.Portrait then self.Portrait:SetAlpha(settings.portraitAlpha) end
	end
end

local OnReputationUpdate = function(self, event, unit, bar)
	local name, id = GetWatchedFactionInfo()
	bar:SetStatusBarColor(FACTION_BAR_COLORS[id].r, FACTION_BAR_COLORS[id].g, FACTION_BAR_COLORS[id].b)
end

local OnUpdateThreat = function(self, event, unit, status)
	-- Hide the threat frame if there's nobody else to have aggro.
	if (GetNumPartyMembers() <= 0 and GetNumRaidMembers() <= 0 and not PetHasActionBar(unit)) then
		self.ThreatFeedbackFrame:Hide()
	end
end

-- If we're showing buffs, suppress the stock Blizz aura display
local HideUnwantedAuras = function(self)
	if Leafed.config.integratePlayerBuffs then
		BuffFrame:Hide()
		TemporaryEnchantFrame:Hide()
	end
end

-- ------------------------------------------------------------------------
-- utility functions to create common elements in a consistent manner
-- ------------------------------------------------------------------------

local createOverlayTexture = function(target, size)
	local overlay = target:CreateTexture(nil, "OVERLAY")
	overlay:SetHeight(size)
	overlay:SetWidth(size)
	return overlay
end

local createBackgroundTexture = function(target)
	target:SetBackdrop(plainBackdrop)
	target:SetBackdropColor(0, 0, 0, .25) 
end

local createStatusBar = function(self, name, width, height, withborder)
	local result = CreateFrame("StatusBar", name, self)

	result:SetHeight(height)
	result:SetWidth(width)

	result:SetStatusBarTexture(settings.textures.bar)
	createBackgroundTexture(result)

	if (withborder) then
		result.Border = CreateFrame("Frame", nil, result)
		result.Border:SetPoint("TOPLEFT", result, "TOPLEFT", -settings.border, settings.border)
		result.Border:SetPoint("BOTTOMRIGHT", result, "BOTTOMRIGHT", settings.border, -settings.border)
		result.Border:SetFrameStrata("BACKGROUND")
		result.Border:SetBackdrop {
			edgeFile = settings.textures.glow, 
			edgeSize = settings.border,
			insets = {left = settings.border, right = settings.border, top = settings.border, bottom = settings.border}
		}
		result.Border:SetBackdropBorderColor(0, 0, 0, 0.5)
	end

	return result
end

local createFadingStatusBar = function(self, name, width, height)
	local result = CreateFrame("StatusBar", name, self)
	result:SetHeight(height)
	result:SetWidth(width)
	result:SetStatusBarTexture(settings.textures.bar)
	return result
end

local createAuraFrame = function(self, location, prefBuffsize, width, height)
	local anchor, xgrowth, ygrowth
	if ("TOP" == location) then
		anchor = "BOTTOMLEFT"; xgrowth="RIGHT"; ygrowth="UP"
	elseif ("BOTTOM" == location) then
		anchor = "TOPLEFT"; xgrowth="RIGHT"; ygrowth="DOWN"
	elseif ("LEFT" == location) then
		anchor = "TOPRIGHT"; xgrowth="LEFT"; ygrowth="DOWN";
	elseif ("RIGHT" == location) then
		anchor = "TOPLEFT"; xgrowth="RIGHT"; ygrowth="DOWN";
	end

	local spacing = 1
	local bsize = math.max((width - 7 * spacing) / 8, prefBuffsize)
	
	local frame = CreateFrame("Frame", nil, self)
	frame.size = bsize
	frame.spacing = spacing
	frame:SetHeight(height)
	frame:SetWidth(width)
	frame.initialAnchor = anchor
	frame["growth-y"] = ygrowth
	frame["growth-x"] = xgrowth
	
	return frame
end

local createBackdrop = function(target)
	-- the backdrop is a faintly dark canvas over the viewport
	target:SetBackdrop(plainBackdrop)
	target:SetBackdropColor(0, 0, 0, .5) 

	target.Border = CreateFrame("Frame", nil, target)
	target.Border:SetPoint("TOPLEFT", target, "TOPLEFT", -settings.frameborder, settings.frameborder)
	target.Border:SetPoint("BOTTOMRIGHT", target, "BOTTOMRIGHT", settings.frameborder, -settings.frameborder)
	target.Border:SetFrameStrata("BACKGROUND")
	target.Border:SetBackdrop {
		edgeFile = settings.textures.glow, 
		edgeSize = settings.frameborder,
		insets = {left = settings.frameborder, right = settings.frameborder, top = settings.frameborder, bottom = settings.frameborder}
	}
	target.Border:SetBackdropBorderColor(0, 0, 0, 0.5)

	-- the plugin shows & hides this frame depending on the threat conditions
	target.ThreatFeedbackFrame = CreateFrame("Frame", nil, target)
	target.ThreatFeedbackFrame:SetPoint("TOPLEFT", target, "TOPLEFT", -settings.frameborder, settings.frameborder)
	target.ThreatFeedbackFrame:SetPoint("BOTTOMRIGHT", target, "BOTTOMRIGHT", settings.frameborder, -settings.frameborder)
	target.ThreatFeedbackFrame:SetFrameStrata("BACKGROUND")
	target.ThreatFeedbackFrame:SetBackdrop {
		edgeFile = settings.textures.glow, 
		edgeSize = settings.frameborder,
		insets = {left = settings.frameborder, right = settings.frameborder, top = settings.frameborder, bottom = settings.frameborder}
	}	
end

-- ------------------------------------------------------------------------
-- add oUF elements or plugin elements to a frame
-- ------------------------------------------------------------------------

local addCombatFeedback = function(self, unit, frame)
	local cbft = frame:CreateFontString(nil, "OVERLAY")
	cbft:SetPoint("CENTER", frame, "CENTER", 0, 0)
	cbft:SetFontObject(GameFontNormal)
	self.CombatFeedbackText = cbft
end

local addComboPoints = function(self, unit, width, height)
	self.CPointBar = CreateFrame("Frame")
	self.CPointBar:SetParent(self)
	self.CPointBar:SetWidth(width)
	self.CPointBar:SetHeight(height)
	
	-- along the bottom edge of the target frame
	if (self.Power) then
		-- backwards compatibility
		self.CPointBar:SetPoint("BOTTOMLEFT", self, "BOTTOMLEFT", 0, 0)
		self.Power:ClearAllPoints()
		self.Power:SetPoint("BOTTOM", self.CPointBar, "TOP", 0, 0)
		self.Health:SetPoint("BOTTOM", self.Power, "TOP", 0, 0)
	end

	self.CPoints = {}
	self.CPoints.unit = PlayerFrame.unit
	for i = 1, 5 do
		self.CPoints[i] = self.CPointBar:CreateTexture(nil, "OVERLAY")
		self.CPoints[i]:SetHeight(settings.player_target.miscbars)
		self.CPoints[i]:SetWidth((settings.player_target.width/5) - 2)
		self.CPoints[i]:SetTexture(settings.textures.bar)
		if i == 1 then
			self.CPoints[i]:SetPoint("TOPLEFT", self.CPointBar, "TOPLEFT")
			self.CPoints[i]:SetVertexColor(196/255,	 30/255,  60/255, 1)
		else
			self.CPoints[i]:SetPoint("LEFT", self.CPoints[i-1], "RIGHT", 2, 0)
		end
	end
	self.CPoints[2]:SetVertexColor(225/255, 136/255, 71/255, 1)
	self.CPoints[3]:SetVertexColor(255/255, 243/255,  82/255, 1)
	self.CPoints[4]:SetVertexColor(213/255, 228/255, 99/255, 1)
	self.CPoints[5]:SetVertexColor(171/255, 255/255, 116/255, 1)
	--self:RegisterEvent("UNIT_COMBO_POINTS", UpdateCPoints)
end

local addHoTIndicators = function(self, unit)
	local i,j,k
	local tex
	local offsetX = -2
	local offsetY = 2
	local anchor, anchorPoint = self.Health, "BOTTOMRIGHT"
	
	local statesToIndicate = aurasByClass[playerClass]
	if (not statesToIndicate) or (#statesToIndicate == 0) then
		return
	end

	self.StateIndicators = {}
	for j = 1,#statesToIndicate do
		k = statesToIndicate[j]
		self.StateIndicators[k] = {}
		for i = 1, stateColors[k].count do
			tex = self.Health:CreateTexture(nil, "OVERLAY")
			tex:SetPoint("BOTTOMRIGHT", anchor, anchorPoint, offsetX, offsetY)
			tex:SetTexture(stateColors[k].color.r, stateColors[k].color.g, stateColors[k].color.b)
			tex:SetHeight(5)
			tex:SetWidth(5)
			tex:Hide()

			table.insert(self.StateIndicators[k], tex)
			anchor, anchorPoint, offsetX, offsetY = tex, "BOTTOMLEFT", -2, 0
		end
	end

	self:RegisterEvent("UNIT_AURA", OnUnitAuraChange)
end

local addPortrait = function(self, unit)
	if not Leafed.config.show3DPortraits then
		return
	end

	self.Portrait = CreateFrame("PlayerModel", nil, self)
	self.Portrait:SetFrameLevel(1)
	self.Portrait:SetWidth(self:GetWidth())
	self.Portrait:SetHeight(self:GetHeight())
	self.Portrait:SetPoint("TOPLEFT", self, "TOPLEFT", 0, 0)
	self.Portrait:SetAlpha(settings.portraitAlpha)
	
	self:RegisterEvent("PLAYER_REGEN_DISABLED", OnUpdateCombat)
	self:RegisterEvent("PLAYER_REGEN_ENABLED", OnUpdateCombat)
end

local addClassIcon = function(self, unit)
	self.ClassIcon = self:CreateTexture(nil, "OVERLAY")
	self.ClassIcon:SetAllPoints(self)
	self.ClassIcon:SetTexture[[Interface\WorldStateFrame\Icons-Classes]]	
	self.ClassIcon:SetAlpha(settings.portraitAlpha)

	self:RegisterEvent("PARTY_MEMBERS_CHANGED", UpdateClassIcon)
	self:RegisterEvent("PLAYER_TARGET_CHANGED", UpdateClassIcon)
	self:RegisterEvent("ARENA_OPPONENT_UPDATE", UpdateClassIcon)
end

local addDebuffHighlight = function(self, target)
	self.DebuffHighlight = target:CreateTexture(nil, "OVERLAY")
	self.DebuffHighlight:SetAllPoints(target)
	self.DebuffHighlight:SetTexture(settings.textures.highlight)
	self.DebuffHighlight:SetVertexColor(0, 0, 0, 0)
	self.DebuffHighlight:SetBlendMode("ADD")
	self.DebuffHighlightAlpha = 1
	self.DebuffHighlightFilter = false
end

local addRuneBar = function(self, width, height)
	self.Runes = CreateFrame("Frame", nil, self)
	self.Runes:SetBackdrop(plainBackdrop)
	self.Runes:SetBackdropColor(0, 0, 0, 0.75)
	self.Runes:SetHeight(height)
	self.Runes:SetWidth(width)
	self.Runes.spacing = 1
	self.Runes.height = height/2
	self.Runes.width = (width / 7) - 8
	self.Runes.anchor = "TOPLEFT"
	self.Runes.growth = "RIGHT"
	
	createBackgroundTexture(self.Runes)

	oUF.colors.runes =
	{
		[1] = {0.69, 0.31, 0.31},
		[2] = {0.33, 0.59, 0.33},
		[3] = {0.31, 0.45, 0.63},
		[4] = {0.84, 0.75, 0.65},
	}

	for i = 1, 6 do
		local bar = CreateFrame("Statusbar", nil, rune)
		bar:SetStatusBarTexture(settings.textures.bar)
		self.Runes[i] = bar
	end
end

local addTotemBar = function(self, width, height)
	self.Totems = CreateFrame("Frame", nil, self)
	self.Totems:SetWidth(width)
	self.Totems:SetHeight(height)
	self.TotemBar = {}
	for i = 1, 4 do
		self.TotemBar[i] = CreateFrame("StatusBar", self:GetName().."_TotemBar"..i, self)
		if(i == 1) then
			self.TotemBar[i]:SetPoint("BOTTOMLEFT", self.Totems, "BOTTOMLEFT", 0, 0)
		else
			self.TotemBar[i]:SetPoint("TOPLEFT", self.TotemBar[i-1], "TOPRIGHT", 1, 0)
		end

		self.TotemBar[i]:SetFrameStrata("BACKGROUND")

		self.TotemBar[i]:SetStatusBarTexture(settings.textures.bar)
		self.TotemBar[i]:SetHeight(height)
		self.TotemBar[i]:SetWidth((width/4) - 1)
		self.TotemBar[i]:SetMinMaxValues(0, 1)

		createBackgroundTexture(self.TotemBar[i])
	end
end

local addReputationBar = function(self, width, height)
	self.Reputation = createStatusBar(self, self:GetName().."_Reputation", width, height, true)
	self.Reputation.PostUpdate = OnReputationUpdate
	self.Reputation.Tooltip = true
end

local addExperienceBar = function(self, width, height)
	self.Experience = createStatusBar(self, self:GetName().."_Experience", width, height, true)
	self.Experience.Tooltip = true
end

local addPowerBar = function(self, unit, width, height)
	self.Power = createFadingStatusBar(self, "Power", width, height, false)

	self.Power.value = self.Power:CreateFontString(nil, "OVERLAY")
	self.Power.value:SetPoint("BOTTOMLEFT", self.Power, "TOPLEFT", -1, 1)
	self.Power.value:SetFont(settings.fonts.base, settings.fonts.basesize - 1)
	self.Power.value:SetTextColor(1,1,1)
	self.Power.value:SetShadowOffset(1, -1)

	-- a few reasonable defaults
	self.Power.Smooth = true
	self.Power.colorTapping = true
	self.Power.colorDisconnected = true
	self.Power.colorClass = true
	self.Power.colorPower = false
	self.Power.colorHappiness = false
	if (unit == "player") then self.Power.frequentUpdates = true end

	-- callbacks
	self.PreUpdatePower = PreUpdatePower
	self.PostUpdatePower = PostUpdatePower
end

local addHealthBar = function(self, unit, width, height)
	self.Health = createFadingStatusBar(self, "Health", width, height)
	
	self.Health:SetStatusBarColor(0.75, 0.0, 0, .5)

	self.Health.status = self.Health:CreateFontString(nil, "ARTWORK")
	self.Health.status:SetFont(settings.fonts.base, settings.fonts.basesize, "THINOUTLINE")
	self.Health.status:SetPoint("TOPLEFT", self.Health, "TOPLEFT", 1, -1)
	self.Health.status:SetTextColor(1,1,1)
	--self.Health.status:SetShadowOffset(1, -1)

	self.Health.value = self.Health:CreateFontString(nil, "OVERLAY")
	self.Health.value:SetFont(settings.fonts.base, settings.fonts.bigsize, "THINOUTLINE")
	self.Health.value:SetPoint("TOPRIGHT", self.Health, "TOPLEFT", -2, 0)
	self.Health.value:SetTextColor(1, 1, 1)
	self.Health.value:SetJustifyH("RIGHT")
	--self.Health.value:SetShadowOffset(1, -1)

	-- a few reasonable defaults
	self.Health.invert = true
	self.Health.frequentUpdates = true
	self.Health.colorClass = true
	self.Health.colorReaction = false
	self.Health.colorDisconnected = true
	self.Health.colorTapping = true
	self.Health.Smooth = false
	self.Health.colorSmooth = false

	-- callbacks
	self.PostUpdateHealth = PostUpdateHealth
end

local addCastbar = function(self, unit, width, height)
	if (not Leafed.config.integrateCastBars) then return end

	self.Castbar = CreateFrame("StatusBar", nil, self)

	self.Castbar:SetWidth(width)
	self.Castbar:SetHeight(height)
	
	self.Castbar:SetStatusBarTexture(settings.textures.bar)
	self.Castbar:SetStatusBarColor(.67, .67, .67, .3)
	--local castTexture = self.Castbar:GetStatusBarTexture()
	--castTexture:SetBlendMode("MOD")
	
	self.Castbar:SetToplevel(true)
	
	self.Castbar.Spark = self.Castbar:CreateTexture(nil, "OVERLAY")
	self.Castbar.Spark:SetTexture("Interface\\CastingBar\\UI-CastingBar-Spark")
	self.Castbar.Spark:SetWidth(8)
	self.Castbar.Spark:SetHeight(self.Castbar:GetHeight() + 4)
	self.Castbar.Spark:SetBlendMode("ADD")

	self.Castbar.Text = self.Castbar:CreateFontString(nil, "OVERLAY")
	self.Castbar.Text:SetPoint("CENTER", self.Castbar, 0, 0)
	self.Castbar.Text:SetShadowOffset(1, -1)
	self.Castbar.Text:SetTextColor(1, 1, 0)
	self.Castbar.Text:SetJustifyH("CENTER")
	self.Castbar.Text:SetHeight(self.Castbar:GetHeight() - 2)
	self.Castbar.Text:SetWidth(self.Castbar:GetWidth() - 2)
	self.Castbar.Text:SetFont(settings.fonts.base, settings.fonts.basesize)
	
	if (self.Health ~= nil) then
		self.Castbar:SetAllPoints(self.Health)
	end
	
	-- icon, TODO configurable?
--	self.Castbar.Icon = self.Castbar:CreateTexture(nil, "BACKGROUND")
--	self.Castbar.Icon:SetWidth(height)
--	self.Castbar.Icon:SetHeight(height)
--	self.Castbar.Icon:SetTexCoord(0.1, 0.9, 0.1, 1)
--	self.Castbar.Icon:SetPoint("TOPLEFT", self.Castbar, "TOPRIGHT", settings.spacing, 0)
end

local addTitleElements = function(self, unit)
	self.Title = CreateFrame("Frame")
	self.Title:SetParent(self)
	self.Title:SetPoint("TOPLEFT", self, "BOTTOMLEFT", 0, 0)
	self.Title:SetPoint("BOTTOMRIGHT", self, "BOTTOMRIGHT", 0, -20)

	self.Level = self.Title:CreateFontString(nil, "OVERLAY")
	self.Level:SetPoint("TOPLEFT", self.Title, 0, -3)
	self.Level:SetJustifyH("LEFT")
	self.Level:SetJustifyV("BOTTOM")
	self.Level:SetFont(settings.fonts.base, settings.fonts.basesize)
	self.Level:SetTextColor(1, 1, 1)
	self.Level:SetShadowOffset(1, -1)
	self.UNIT_LEVEL = OnUpdateLevel

	self.Name = self.Title:CreateFontString(nil, "OVERLAY")
	self.Name:SetFont(settings.fonts.base, settings.fonts.basesize)
	self.Name:SetJustifyH("LEFT")
	self.Name:SetJustifyV("BOTTOM")
	self.Name:SetShadowOffset(1, -1)
	self.Name:SetHeight(20) -- truncate at one line
	self.Name:SetPoint("BOTTOMLEFT", self.Level, "BOTTOMRIGHT", 2, 0)
	self.UNIT_NAME_UPDATE = OnUpdateName
end

local addStateIcons = function(self, target)
	self.Combat = createOverlayTexture(target, 20)
	self.Combat:SetPoint("TOPRIGHT", target, "TOPRIGHT", 0, 0)
	self.Combat:SetTexture("Interface\\CharacterFrame\\UI-StateIcon")
	self.Combat:SetTexCoord(0.58, 0.90, 0.08, 0.41)

	self.Resting = createOverlayTexture(target, 20)
	self.Resting:SetPoint("TOPRIGHT", self.Combat, "TOPLEFT", -2, 0)
	self.Resting:SetTexture("Interface\\CharacterFrame\\UI-StateIcon")
	self.Resting:SetTexCoord(0, 0.5, 0, 0.421875)
end

local addRoleIcons = function(self, target)
	self.Leader = createOverlayTexture(target, 12)
	self.Leader:SetPoint("TOP", target, "TOP", 0, 4)
	
	self.PvP = createOverlayTexture(target, 40)
	self.PvP:SetPoint("TOPLEFT", self.Leader, "TOPRIGHT", 1, 0)

	self.MasterLooter = createOverlayTexture(target, 12)
	self.MasterLooter:SetPoint("TOPRIGHT", self.Leader, "TOPLEFT", -1, 0)

	local h = self:GetHeight()
	self.LFDRole  = self:CreateTexture(nil, "ARTWORK")
	self.LFDRole:SetHeight(20)
	self.LFDRole:SetWidth(20)
	self.LFDRole:SetPoint("TOPRIGHT", self.Leader, "TOPLEFT", -2, 0)

	self.RaidIcon = createOverlayTexture(target, 20)
	self.RaidIcon:SetPoint("TOPLEFT", self.Health, "TOPLEFT", 1, -1)
end

-- ------------------------------------------------------------------------
-- build frame core; items every frame has.
-- ------------------------------------------------------------------------

local initializeFrame = function(self, unit, rules)
	if unit then self.unitGUID = UnitGUID(unit) end
	self.menu = menu -- Enable the menus
	self.MoveableFrames = true -- support for oUF_MoveableFrames

	self:SetScript("OnEnter", UnitFrame_OnEnter)
	self:SetScript("OnLeave", UnitFrame_OnLeave)
	
	self:RegisterForClicks"anyup"
	self:SetAttribute("*type2", "menu")

	self:SetFrameStrata("BACKGROUND")

	self:SetHeight(rules.height)
	self:SetWidth(rules.width)

	createBackdrop(self)

	self.Highlight = self:CreateTexture(nil, "HIGHLIGHT")
	self.Highlight:SetAllPoints(self)
	self.Highlight:SetTexture("Interface\\ChatFrame\\ChatFrameBackground")
	self.Highlight:SetVertexColor(0.84, 0.75, 0.65, 0.15)
	self.Highlight:SetBlendMode("ADD")

	-- support for oUF_SpellRange
	self.outsideRangeAlpha = 0.4
	self.inRangeAlpha = 1
	self.Range = true 
	self.SpellRange = true

	self:EnableElement('ThreatFeedback')
	self.PostUpdateThreat = OnUpdateThreat
end

local buildPlayerFrame = function(self, unit)
	local rules = settings.player_target
	initializeFrame(self, unit, rules)
		
	addPortrait(self, unit)
	
	local currentAnchor = self
	local remainingHeight = rules.height

	-- power bar
	addPowerBar(self, unit, rules.width, rules.power)
	remainingHeight = remainingHeight - rules.power
	self.Power:SetPoint("BOTTOMLEFT", currentAnchor, "BOTTOMLEFT", 0, 0)
	currentAnchor = self.Power

	-- add required class bars
	if (playerClass == "DRUID") then
		self:RegisterEvent("UNIT_MANA", UpdateDruidPower)
		self:RegisterEvent("UNIT_ENERGY", UpdateDruidPower)
		self:RegisterEvent("PLAYER_LOGIN", UpdateDruidPower)
		self:RegisterEvent("UPDATE_SHAPESHIFT_FORM", UpdateDruidPower)
	elseif (playerClass == "DEATHKNIGHT") then
		addRuneBar(self, rules.width, rules.miscbars)
		self.Runes:SetPoint("BOTTOMLEFT", currentAnchor, "TOPLEFT", 0, 0)
		currentAnchor = self.Runes
		remainingHeight = remainingHeight - rules.miscbars
	elseif (IsAddOnLoaded("oUF_TotemBar") and playerClass=="SHAMAN") then
		addTotemBar(self, rules.width, rules.miscbars)
		self.Totems:SetPoint("BOTTOMLEFT", currentAnchor, "TOPLEFT", 0, 0)
		currentAnchor = self.Totems
		remainingHeight = remainingHeight - rules.miscbars
	end

	-- health bar
	addHealthBar(self, unit, rules.width, remainingHeight)
	self.Health:SetPoint("BOTTOMLEFT", currentAnchor, "TOPLEFT", 0, 0)
	self.Health:SetPoint("TOPRIGHT", self, "TOPRIGHT", 0, 0)
	self.Health.colorClass = false
	self.Health.colorDisconnected = false
	self.Health.colorTapping = false
	
	self.Power.value:ClearAllPoints()
	self.Power.value:SetPoint("TOPRIGHT", self.Health, "TOPRIGHT", -1, -1)

	addCastbar(self, unit, rules.width, remainingHeight)

	addDebuffHighlight(self, self.Health)
	addHoTIndicators(self, unit)

	addTitleElements(self, unit)
	addStateIcons(self, self.Title)
	addRoleIcons(self, self.Health)

	currentAnchor = nil

	if IsAddOnLoaded("oUF_Experience") then
		addExperienceBar(self, (rules.width - settings.gutter) / 2, 7)
		self.Experience:SetPoint("BOTTOMLEFT", self, "TOPLEFT", 0, settings.gutter)
		currentAnchor = self.Experience
	end
	
	if IsAddOnLoaded("oUF_Reputation") then
		addReputationBar(self, (rules.width - settings.gutter) / 2, 7)
		if (currentAnchor) then
			self.Reputation:SetPoint("TOPLEFT", currentAnchor, "TOPRIGHT", settings.gutter, 0)
		else
			self.Reputation:SetPoint("BOTTOMLEFT", self, "TOPLEFT", 0, settings.gutter)
			currentAnchor = self.Reputation
		end
	end

--	if (Leafed.config.integratePlayerDebuffs) then
--		-- debuffs below bars
--		self.Debuffs = createAuraFrame(self, "BOTTOM", rules.aura, rules.width, 5 * rules.aura)
--		self.Debuffs:SetPoint("TOPLEFT", self, "BOTTOMLEFT", 0, -settings.gutter)
--	end
--	
--	if (Leafed.config.integratePlayerBuffs) then
--		-- buffs above
--		self.Buffs = createAuraFrame(self, "TOP", rules.aura, rules.width, 5 * rules.aura)
--		self.Buffs:SetPoint("BOTTOMLEFT", currentAnchor, "TOPLEFT", 0, settings.gutter)
--	end
--		
--	-- but always put the weapon enchant to the right
--	if (IsAddOnLoaded("oUF_WeaponEnchant")) then
--		self.Enchant = createAuraFrame(self, "RIGHT", rules.aura, rules.aura * 2 + 2, rules.aura)
--		self.Enchant:SetPoint("TOPLEFT", self, "TOPRIGHT", settings.gutter, 0)
--	end
	
	addCombatFeedback(self, unit, self.Health)

	-- enable some more oUF plugin features
--	self:EnableElement('WeaponEnchant')
	self:EnableElement('Reputation')
	self:EnableElement('Experience')

	HideUnwantedAuras(self)
end

local buildTargetFrame = function(self, unit)
	local rules = settings.target_focus
	initializeFrame(self, unit, rules)

	addPortrait(self, unit)

	local currentAnchor = self
	
	if (playerClass=="ROGUE" or playerClass=="DRUID") then
		addComboPoints(self, unit, rules.width, rules.miscbars)
		self.CPointBar:SetPoint("BOTTOMLEFT", currentAnchor, "BOTTOMLEFT", 0, 0)
		currentAnchor = self.CPointBar
	end

	-- power bar
	addPowerBar(self, unit, rules.width, rules.power)
	if (currentAnchor == self) then
		self.Power:SetPoint("BOTTOMLEFT", self, "BOTTOMLEFT", 0, 0)
	else
		self.Power:SetPoint("BOTTOMLEFT", currentAnchor, "TOPLEFT", 0, 0)
	end
	
	-- health bar
	addHealthBar(self, unit, rules.width, rules.height - rules.power)
	self.Health:SetPoint("BOTTOMLEFT", self.Power, "TOPLEFT", 0, 0)
	self.Health:SetPoint("TOPRIGHT", self, "TOPRIGHT", 0, 0)
	self.Health.colorClass = false
	self.Health.Smooth = true
	self.Health.colorSmooth = true
	
	addCastbar(self, unit, rules.width, rules.height - rules.power)
	
	self.Power.value:ClearAllPoints()
	self.Power.value:SetPoint("TOPRIGHT", self.Health, "TOPRIGHT", -1, -1)

	addDebuffHighlight(self, self.Health)
	addHoTIndicators(self, unit)
	addTitleElements(self)
	addRoleIcons(self, self.Health)

	if (Leafed.config.integrateTargetBuffs) then
		local debuffAnchor	

		-- buffs below the frame, 1 row
		self.Buffs = createAuraFrame(self, "BOTTOM", rules.aura, 8 * rules.aura, rules.aura)
		self.Buffs:SetPoint("TOPLEFT", self, "BOTTOMLEFT", 0, -settings.gutter)
		self.Buffs.onlyShowPlayer = Leafed.config.filterTargetBuffs
		
		-- debuffs above frame (more debuffs for bosses; more space above)
		self.Debuffs = createAuraFrame(self, "TOP", rules.aura, rules.width, 3 * rules.aura)
		self.Debuffs:SetPoint("BOTTOMLEFT", self, "TOPLEFT", 0, (settings.targets.height + 2 * settings.gutter))
	end
	
	addCombatFeedback(self, unit, self.Health)

	-- the target frame gets extra info
	self.UnitDescription = self.Title:CreateFontString(nil, "OVERLAY")
	self.UnitDescription:SetFont(settings.fonts.base, settings.fonts.basesize - 1)
	self.UnitDescription:SetJustifyH("LEFT")
	self.UnitDescription:SetJustifyV("BOTTOM")
	self.UnitDescription:SetShadowOffset(1, -1)
	self.UnitDescription:SetHeight(20) -- truncate at one line
	self.UNIT_NAME_UPDATE = OnUpdateNameDetailed

	-- enable some more oUF plugin features
	self:EnableElement('ThreatFeedback')
	self.PostUpdateThreat = OnUpdateThreat
	self:EnableElement('WeaponEnchant')
	self:EnableElement('Reputation')
	self:EnableElement('Experience')
end

local buildPartyMemberFrame = function(self, unit)
	local rules = settings.party
	initializeFrame(self, unit, rules)

	addPortrait(self, unit)

	-- power bar at the bottom
	addPowerBar(self, unit, rules.width, rules.power)
	self.Power:SetPoint("BOTTOMLEFT", self, "BOTTOMLEFT", 0, 0)

	-- health bar takes up the rest of the space
	addHealthBar(self, unit, rules.width, rules.height - rules.power)
	self.Health:SetPoint("TOPLEFT", self, "TOPLEFT", 0, 0)
	self.Health:SetPoint("BOTTOMRIGHT", self.Power, "TOPRIGHT", 0, 0)
	self.Health.value:SetFont(settings.fonts.base, settings.fonts.bigsize - 3, "THINOUTLINE")
	self.Health.status:SetFont(settings.fonts.base, settings.fonts.basesize - 1)
	self.Health.colorClass = false
	self.Health.Smooth = true
	self.Health.colorSmooth = true

	addCastbar(self, unit, rules.width, rules.height - rules.power)

	self.Power.value:ClearAllPoints()
	self.Power.value:SetPoint("TOPRIGHT", self.Health, "TOPRIGHT", -1, -1)
	self.Power.value:SetFont(settings.fonts.base, settings.fonts.basesize - 1)

	addDebuffHighlight(self, self.Health)
	addHoTIndicators(self, unit)
	addTitleElements(self, unit)
	addRoleIcons(self, self.Health)

	self.PostUpdatePower = PostUpdatePower
	self:EnableElement('ThreatFeedback')
end

local buildFocusFrame = function(self, unit)
	buildTargetFrame(self, unit)
end

local buildPlayerPetFrame = function(self, unit)
	local rules = settings.pet_focus
	initializeFrame(self, unit, rules)
	addPortrait(self, unit)
	
	-- health bar on top 
	addHealthBar(self, unit, rules.width, rules.health)
	self.Health:SetPoint("TOPLEFT", self, "TOPLEFT", 0, 0)
	self.Health.invert = true
	self.Health.colorClass = false
	self.Health.value:SetFont(settings.fonts.base, settings.fonts.basesize - 2)
	self.Health.value:SetJustifyV("BOTTOM")
	self.Health.status:Hide()

	-- power bar below health bar
	addPowerBar(self, unit, rules.width, rules.power)
	self.Power:SetPoint("TOPLEFT", self.Health, "BOTTOMLEFT", 0, 0)
	self.Power.value:Hide()
	
	-- cast bar over the health bar
	addCastbar(self, unit, rules.width, rules.health)
	
	addTitleElements(self, unit)
	addRoleIcons(self, self.Health)	
	addDebuffHighlight(self, self.Health)
	addHoTIndicators(self, unit)

	self.Level:Hide()

	-- adjust the name
	self.Name:SetFont(settings.fonts.base, settings.fonts.basesize - 2)
	self.Name:ClearAllPoints()
	self.Name:SetPoint("TOPLEFT", self, "TOPLEFT", 2, -2)
	self.Name:SetPoint("BOTTOMRIGHT", self, "BOTTOMRIGHT", -2, 2)
	self.Name:SetJustifyV("TOP")
	self.Name:SetHeight(self:GetHeight() - 4)

	if (playerClass=="HUNTER") then
		self.Health.colorHappiness = true 
	end
end

local buildSmallTargetFrame = function(self, unit)
	local rules = settings.targets
	rules.height = settings.player_target.height

	initializeFrame(self, unit, rules)
	self:SetBackdropColor(0, 0, 0, .25)
	addPortrait(self, unit)

	addTitleElements(self, unit)
	self.Name:SetFont(settings.fonts.base, settings.fonts.basesize - 2)
	self.Name:ClearAllPoints()
	self.Name:SetPoint("TOPLEFT", self.Title, "TOPLEFT", 0, -3)
	self.Name:SetWidth(self:GetWidth() * 3)
	self.Name:SetHeight(20)
	self.Name:SetJustifyV("TOP")
	self.Level:Hide()

	addHealthBar(self, unit, rules.width, rules.height)
	self.Health:SetAllPoints(self)
	self.Health.value:ClearAllPoints()
	self.Health.value:SetPoint("BOTTOMLEFT", self.Health, "BOTTOMLEFT", 1, 2)
	self.Health.value:SetFont(settings.fonts.base, settings.fonts.basesize - 2)
	self.Health.status:Hide()

	addPowerBar(self, unit, rules.width, rules.height)
	self.Power:Hide()
end

local buildPartySubitemFrame = function(self, unit)
	local rules = settings.partysubitem
	initializeFrame(self, unit, rules)
	self:SetBackdropColor(0, 0, 0, .25)
	
	addTitleElements(self, unit)
	self.Name:SetFont(settings.fonts.base, settings.fonts.basesize - 2)
	self.Name:ClearAllPoints()
	self.Name:SetPoint("TOPLEFT", self, "BOTTOMLEFT", 0, -settings.gutter)
	self.Name:SetJustifyV("TOP")
	self.Level:Hide()

	addHealthBar(self, unit, rules.width, rules.height)
	self.Health:SetAllPoints(self)
	self.Health.frequentUpdates = false
	self.Health.value:Hide()
	self.Health.status:Hide()

	addPowerBar(self, unit, rules.width, rules.height)
	self.Power:Hide()

	addDebuffHighlight(self, self.Health)
	addHoTIndicators(self, unit)
end

local buildGenericFrame = function(self, unit)
	local rules = settings.generic
	initializeFrame(self, unit, rules)
	self:SetBackdropColor(0, 0, 0, .75)
	
	addHealthBar(self, unit, rules.width, rules.height - rules.power)
	self.Health.compact = true
	self.Health:SetPoint("TOPLEFT", self, "TOPLEFT", 0, 0)
	self.Health.status:Hide()
	self.Health.value:ClearAllPoints()
	self.Health.value:SetAllPoints(self.Health)
	self.Health.value:SetFont(settings.fonts.base, settings.fonts.basesize - 2)
	self.Health.value:SetJustifyV("BOTTOM")
	self.Health.value:SetJustifyH("LEFT")
	self.Health.frequentUpdates = false
	self.Health.colorClass = false

	addTitleElements(self, unit)
	self.Title:ClearAllPoints()
	self.Title:SetPoint("TOPLEFT", self, "TOPLEFT", 0, 0)
	self.Title:SetPoint("BOTTOMRIGHT", self, "BOTTOMRIGHT", 0, 0)
	self.Name:SetFont(settings.fonts.base, settings.fonts.basesize - 2)
	self.Name:SetAllPoints(self.Health)
	self.Name:SetJustifyV("TOP")
	self.Name:SetJustifyH("RIGHT")
	self.Level:Hide()

	addPowerBar(self, unit, rules.width, rules.power)
	self.Power:SetPoint("BOTTOMLEFT", self, "BOTTOMLEFT", 0, 0)
	self.Power.value:Hide()

	addRoleIcons(self, self.Health) -- TODO adjust locations
	addDebuffHighlight(self, self.Health)
	addHoTIndicators(self, unit)
	
	self.PvP:SetWidth(12)
	self.PvP:SetHeight(12)
end

local buildArenaFrame = function(self, unit)
	-- Nearly identical to party frames, but not quite.
	local rules = settings.party
	initializeFrame(self, unit, rules)

	-- power bar at the bottom
	addPowerBar(self, unit, rules.width, rules.power)
	self.Power:SetPoint("BOTTOMLEFT", self, "BOTTOMLEFT", 0, 0)
	self.Power.value:SetFont(settings.fonts.base, settings.fonts.basesize - 1)

	addCastbar(self, unit, rules.width, rules.health)

	-- health bar takes up the rest of the space
	addHealthBar(self, unit, rules.width, rules.height - rules.power)
	self.Health:SetPoint("TOPLEFT", self, "TOPLEFT", 0, 0)
	self.Health:SetPoint("BOTTOMRIGHT", self.Power, "TOPRIGHT", 0, 0)
	self.Health.value:SetFont(settings.fonts.base, settings.fonts.basesize - 1)
	self.Health.status:SetFont(settings.fonts.base, settings.fonts.basesize - 1)
	self.Health.colorClass = false

	addClassIcon(self, unit) -- note
	addTitleElements(self, unit)
	addRoleIcons(self, self.Health)

	addDebuffHighlight(self, self.Health)
	addHoTIndicators(self, unit)

	self.PostUpdatePower = PostUpdatePower
end

-- ------------------------------------------------------------------------
-- layout v2
-- ------------------------------------------------------------------------
local buildFramesByType = function(self, unit)
	if ("player" == unit) then
		buildPlayerFrame(self, unit)
	elseif ("target" == unit) then
		buildTargetFrame(self, unit)
	elseif ("focus" == unit) then
		buildFocusFrame(self, unit)
	elseif ("pet" == unit) then
		buildPlayerPetFrame(self, unit)
	elseif (self:GetParent():GetName():match"oUF_Party") then
		buildPartyMemberFrame(self, unit)
	elseif (("pettarget" == unit) or ("focustarget" == unit)) then
		buildSmallTargetFrame(self, unit)
	elseif ("targettarget" == unit) then
		buildSmallTargetFrame(self, unit)
	elseif (unit ~= nil) and (unit:match("party.+target") or (unit:match("partypet")) or unit:match("arena.+target") ) then
		buildPartySubitemFrame(self, unit)
	elseif (self:GetName():match"oUF_Arena") then
		buildArenaFrame(self, unit)
	else
		buildGenericFrame(self, unit)
	end
	
	return self
end

-- ------------------------------------------------------------------------
-- spawn frames & set their initial locations
-- ------------------------------------------------------------------------

local spawnFrames = function()
	oUF:RegisterStyle("Leafed", buildFramesByType)
	oUF:SetActiveStyle("Leafed")

	local player = oUF:Spawn("player", "oUF_Player")
	oUF_Player:SetScale(Leafed.config.scale)
	oUF_Player:SetPoint("CENTER", settings.player_target.x, settings.player_target.y)
	
	local target = oUF:Spawn("target", "oUF_Target")
	oUF_Target:SetScale(Leafed.config.scale)
	oUF_Target:SetPoint("CENTER", -settings.player_target.x, settings.player_target.y)
	local tot = oUF:Spawn("targettarget", "oUF_TargetTarget")
	oUF_TargetTarget:SetScale(Leafed.config.scale)
	oUF_TargetTarget:SetPoint("TOPLEFT", oUF_Target, "TOPRIGHT", settings.gutter, 0)
	
	local pet = oUF:Spawn("pet", "oUF_Pet")
	oUF_Pet:SetScale(Leafed.config.scale)
	oUF_Pet:SetPoint("TOPLEFT", oUF_TargetTarget, "TOPRIGHT", 50, 0)
	local pettarget = oUF:Spawn("pettarget", "oUF_PetTarget")
	oUF_PetTarget:SetScale(Leafed.config.scale)
	oUF_PetTarget:SetPoint("TOPLEFT", oUF_Pet, "TOPRIGHT", settings.gutter, 0)
	
	local focus = oUF:Spawn("focus", "oUF_Focus")
	oUF_Focus:SetScale(Leafed.config.scale)
	oUF_Focus:SetPoint("BOTTOMLEFT", oUF_Target, "TOPLEFT", 0, 25) -- TODO magic number
	local focustarget = oUF:Spawn("focustarget", "oUF_FocusTarget")
	oUF_FocusTarget:SetScale(Leafed.config.scale)
	oUF_FocusTarget:SetPoint("TOPLEFT", oUF_Focus, "TOPRIGHT", settings.gutter, 0)
	
	local party = oUF:Spawn("header", "oUF_Party")
	oUF_Party:SetScale(Leafed.config.scale)
	oUF_Party:SetPoint("TOPLEFT", oUF_Target, "BOTTOMLEFT", 0, -40) -- TODO magic numbers
	party:Show()

	party:SetAttribute("showParty", true)
	party:SetManyAttributes("yOffset", -settings.party.height)
	local partytarget = {}
	local partypet = {}
	for i = 1, 4 do
		partytarget[i] = oUF:Spawn("party"..i.."target", "oUF_Party"..i.."Target")
		if i == 1 then
			partytarget[i]:SetPoint("TOPLEFT", party, "TOPRIGHT", settings.gutter, 0)
		else
			partytarget[i]:SetPoint("TOPLEFT", partytarget[i-1], "TOPLEFT", 0, -2 * settings.party.height)
		end
		getglobal("oUF_Party"..i.."Target"):SetScale(Leafed.config.scale)

		partypet[i] = oUF:Spawn("partypet"..i, "oUF_PartyPet"..i)
		partypet[i]:SetPoint("TOPLEFT", partytarget[i], "TOPRIGHT", settings.gutter, 0)
		getglobal("oUF_PartyPet"..i):SetScale(Leafed.config.scale)
	end
	
	-- now spawn raid frames
	local raid = {}
	local raidx, raidy = -1, -1
	-- TODO this is not configurable
	local groupheight = 5 * (settings.generic.height + 10) + settings.raid.rowspacing 

	for i = 1, NUM_RAID_GROUPS do	
		local raidgroup = oUF:Spawn("header", "oUF_Raid" .. i)
		raidgroup:SetManyAttributes("groupFilter", tostring(i), "showRaid", true, "yOffset", -settings.gutter)
		table.insert(raid, raidgroup)

		if (i == 1) then
			-- TODO magic number
			raidgroup:SetPoint("TOPLEFT", oUF_Player, "BOTTOMRIGHT", 77, -40) -- TODO same magic numbers
			raidx = raidgroup:GetLeft()
			raidy = raidgroup:GetTop()
		elseif (mod(i - 1, settings.raid.columns) == 0) then
			raidgroup:SetPoint("TOPLEFT", raid[i - settings.raid.columns], "TOPLEFT", 0, -groupheight)
		else
			raidgroup:SetPoint("TOPLEFT", raid[i - 1], "TOPRIGHT", settings.raid.colspacing, 0)
		end
	
		getglobal("oUF_Raid"..i):SetScale(Leafed.config.scale)
		raidgroup:Show()
	end	

	-- arena frames -- 
	local arena = {}
	local arenatargets = {}
	for i = 1, 5 do
		arena[i] = oUF:Spawn("arena"..i, "oUF_Arena"..i)
		arenatargets[i] = oUF:Spawn("arena"..i.."target", "oUF_Arena"..i.."Target")
		if i == 1 then
			arena[i]:SetPoint("TOPLEFT", partypet[1], "BOTTOMLEFT", 0, -settings.gutter)
		else
			arena[i]:SetPoint("TOPLEFT", arena[i-1], "TOPRIGHT", settings.gutter, 0)
		end
		arenatargets[i]:SetPoint("TOPLEFT", arena[i], "BOTTOMLEFT", 0, -settings.gutter)
	end

	local partyToggle = CreateFrame("Frame")
	partyToggle:RegisterEvent("PLAYER_LOGIN")
	partyToggle:RegisterEvent("RAID_ROSTER_UPDATE")
	partyToggle:RegisterEvent("PARTY_LEADER_CHANGED")
	partyToggle:RegisterEvent("PARTY_MEMBERS_CHANGED")

	partyToggle:SetScript("OnEvent", function(self)
		if (InCombatLockdown()) then
			self:RegisterEvent("PLAYER_REGEN_ENABLED")
		else
			self:UnregisterEvent("PLAYER_REGEN_ENABLED")
			local numraid = GetNumRaidMembers()
			if numraid > 0 and (numraid > 5 or numraid ~= GetNumPartyMembers() + 1) then
				party:Hide()
				for i,v in ipairs(raid) do v:Show() end
				for i,v in ipairs(partypet) do v:Disable()	end
				for i,v in ipairs(partytarget) do v:Disable()	end
			else
				party:Show()
				for i,v in ipairs(raid) do v:Hide() end
				for i,v in ipairs(partypet) do v:Enable()	end
				for i,v in ipairs(partytarget) do v:Enable() end
			end
		end
	end)
	
end

spawnFrames()
